﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM.HTML
{
    #region Using Statements

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using KLF.Lib.Browser.DOM.Events;

    #endregion

    public class HTMLFormElement : HTMLElement
    {
        public const string DefaultFormEncoding = "application/x-www-form-urlencoded";

        #region Constructors

        internal HTMLFormElement(Document owner)
            : base(owner, HTMLConstants.HTML_TAG_FORM)
        {
        }

        #endregion

        #region DOM Level 2 HTML Compliance

        /// <summary>
        /// List of character sets supported by the server. See the accept-
        /// charset attribute definition in HTML 4.01.
        /// </summary>
        public string acceptCharset
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_ACCEPTCHARSET);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_ACCEPTCHARSET, value);
            }
        }

        /// <summary>
        /// Server-side form handler. See the action attribute definition 
        /// in HTML 4.01.
        /// </summary>
        public string action
        {
            get
            {
                return String.IsNullOrEmpty(getAttribute(HTMLConstants.HTML_ATT_ACTION)) ?
                    Owner.documentURI : getAttribute(HTMLConstants.HTML_ATT_ACTION);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_ACTION, value);
            }
        }
        /// <summary>
        /// Returns a collection of all form control elements in the form.
        /// </summary>
        public HTMLCollection elements
        {
            get
            {
                return new HTMLCollection(Owner, Elements
                    .Where(e =>
                        e.tagName.Equals(HTMLConstants.HTML_TAG_INPUT) ||
                        e.tagName.Equals(HTMLConstants.HTML_TAG_SELECT) ||
                        e.tagName.Equals(HTMLConstants.HTML_TAG_ISINDEX) ||
                        e.tagName.Equals(HTMLConstants.HTML_TAG_BUTTON) ||
                        e.tagName.Equals(HTMLConstants.HTML_TAG_TEXTAREA))
                    .Cast<Node>());
            }
        }

        /// <summary>
        /// The content type of the submitted form, generally 
        /// "application/x-www-form-urlencoded". See the enctype attribute 
        /// definition in HTML 4.01.
        /// 
        /// Note: The onsubmit event handler is not guaranteed to be triggered 
        /// when invoking this method. The behavior is inconsistent for 
        /// historical reasons and authors should not rely on a particular one.
        /// </summary>
        public string enctype
        {
            get
            {
                return String.IsNullOrEmpty(getAttribute(HTMLConstants.HTML_ATT_ENCTYPE)) ?
                    DefaultFormEncoding : getAttribute(HTMLConstants.HTML_ATT_ENCTYPE);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_ENCTYPE, value);
            }
        }

        /// <summary>
        /// The number of form controls in the form.
        /// </summary>
        public int length
        {
            get
            {
                return elements.length;
            }
        }

        /// <summary>
        /// HTTP method [IETF RFC 2616] used to submit form. See the method 
        /// attribute definition in HTML 4.01.
        /// </summary>
        public string method
        {
            get
            {
                return String.IsNullOrEmpty(getAttribute(HTMLConstants.HTML_ATT_METHOD)) 
                    ? "GET" : getAttribute(HTMLConstants.HTML_ATT_METHOD);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_METHOD, value);
            }
        }

        /// <summary>
        /// Names the form
        /// </summary>
        public string name
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_NAME);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_NAME);
                else
                    setAttribute(HTMLConstants.HTML_ATT_NAME, value);
            }
        }

        /// <summary>
        /// Restores a form element's default values. It performs the same 
        /// action as a reset button. 
        /// </summary>
        public void reset()
        {
            HTMLCollection list = elements;

            for (int i = 0; i < list.length; i++)
            {
                HTMLElement e = list.item(i) as HTMLElement;

                if (e != null)
                {
                    e.setAttribute(HTMLConstants.HTML_ATT_VALUE, 
                        e.getAttribute(HTMLConstants.HTML_ATT_DEFAULT_VALUE));
                }
            }
        }

        internal void submit(HTMLControlBase submitter)
        {
            Event eventSubmit = Owner.createEvent(HTMLConstants.HTML_EVENT_HTMLEVENTS);
            eventSubmit.initEvent(HTMLConstants.HTML_EVENT_SUBMIT, true, true);
            eventSubmit.target = this;

            if (!dispatchEvent(eventSubmit))
                return;

            RequestContext postContext = Owner.Context.ResolveNewContext(this.action ?? "");
            postContext.Verb = method;
            foreach (HTMLControlBase c in elements._Nodes.Cast<HTMLControlBase>())
            {
                //
                if (c is HTMLInputElement)
                {
                    HTMLInputElement i = c as HTMLInputElement;
                    if (i.type == HTMLConstants.HTML_INPUT_SUBMIT)
                    {
                        if (i != submitter)
                            continue;
                        postContext.PostVariables.Add(c.name, c.value);
                    }
                    else if (i.type == HTMLConstants.HTML_INPUT_IMAGE)
                    {
                        if (i != submitter)
                            continue;
                        postContext.PostVariables.Add(c.name, c.value);
                        postContext.PostVariables.Add(c.name + ".x", "1");
                        postContext.PostVariables.Add(c.name + ".y", "1");
                    }
                    else if (i.type == HTMLConstants.HTML_INPUT_RESET)
                        continue;
                    else if (i.type == HTMLConstants.HTML_INPUT_RADIO ||
                        i.type == HTMLConstants.HTML_INPUT_RADIO)
                    {
                        if (i.@checked)
                        {
                            if (postContext.PostVariables.ContainsKey(c.name))
                            {
                                Console.WriteLine("WARNING: Control {0} already has value {1}; Tried to set to {2}",
                                    c.name, postContext.PostVariables[c.name], c.value);
                            }
                            postContext.PostVariables[c.name] = c.value ?? "on";
                        }
                    }
                    else
                    {
                        if (postContext.PostVariables.ContainsKey(c.name))
                        {
                            Console.WriteLine("WARNING: Control {0} already has value {1}; Tried to set to {2}",
                                c.name, postContext.PostVariables[c.name], c.value);
                        }
                        postContext.PostVariables[c.name] = c.value;
                    }
                }
                else if (c is HTMLButtonElement)
                {
                    HTMLButtonElement b = c as HTMLButtonElement;
                    if (b.type == HTMLConstants.HTML_BUTTON_IMAGE)
                    {
                        if (b != submitter)
                            continue;
                        postContext.PostVariables.Add(c.name, c.value);
                        postContext.PostVariables.Add(c.name + ".x", "1");
                        postContext.PostVariables.Add(c.name + ".y", "1");
                    }
                    if (b.type == HTMLConstants.HTML_BUTTON_SUBMIT)
                    {
                        if (b != submitter)
                            continue;
                        postContext.PostVariables.Add(c.name, c.value);
                    }
                    else if (b.type == HTMLConstants.HTML_BUTTON_RESET)
                        continue;
                }
                else
                {
                    postContext.PostVariables.Add(c.name, c.value);
                }
            }
            Owner.Context.Window.ExecuteRequest(postContext);
        }

        /// <summary>
        /// Submits the form. It performs the same action as a submit button.
        /// </summary>
        public void submit()
        {
            submit(null);
        }

        /// <summary>
        /// Frame to render the resource in. See the target attribute 
        /// definition in HTML 4.01.
        /// </summary>
        public string target
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_TARGET);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_TARGET);
                else
                    setAttribute(HTMLConstants.HTML_ATT_TARGET, value);
            }
        }

        #endregion
    }
}
